<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=yes" />
    <style>
      html {
        font-family: BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
        -webkit-font-smoothing: antialiased;
        background-color: #fff;
        font-size: 16px;
      }
      body {
        color: #4a4a4a;
        margin: 8px;
        font-size: 1em;
        font-weight: 400;
      }
      header {
        margin-bottom: 8px;
        display: flex;
        flex-direction: column;
      }
      main {
        width: 100%;
        display: flex;
        flex-direction: column;
      }
      a {
        color: #3273dc;
        cursor: pointer;
        text-decoration: none;
      }
      a:hover {
        color: #000;
      }
      button {
        color: #fff;
        background-color: #3298dc;
        border-color: transparent;
        cursor: pointer;
        text-align: center;
      }
      button:hover {
        background-color: #2793da;
        flex: none;
      }
      .spacer {
        flex: auto;
      }
      .small {
        font-size: 0.75rem;
      }
      footer {
        margin-top: 16px;
        display: flex;
        align-items: center;
      }
      .header-label {
        margin-right: 4px;
      }
      .benchmark-set {
        margin: 8px 0;
        width: 100%;
        display: flex;
        flex-direction: column;
      }
      .benchmark-title {
        font-size: 3rem;
        font-weight: 600;
        word-break: break-word;
        text-align: center;
      }
      .benchmark-graphs {
        display: flex;
        flex-direction: row;
        justify-content: space-around;
        align-items: center;
        flex-wrap: wrap;
        width: 100%;
      }
      .benchmark-chart {
        max-width: 1000px;
      }
    </style>
    <title>Benchmarks</title>
  </head>

  <body>
    <header id="header">
      <div class="header-item">
        <strong class="header-label">Last Update:</strong>
        <span id="last-update"></span>
      </div>
      <div class="header-item">
        <strong class="header-label">Repository:</strong>
        <a id="repository-link" rel="noopener"></a>
      </div>
    </header>
    <main id="main"></main>
    <footer>
      <button id="dl-button">Download data as JSON</button>
      <div class="spacer"></div>
      <div class="small">Powered by <a rel="noopener" href="https://github.com/marketplace/actions/continuous-benchmark">github-action-benchmark</a></div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.2/dist/Chart.min.js"></script>
    <script src="data.js"></script>
    <script id="main-script">
      'use strict';
      (function() {
        // Colors from https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
        const toolColors = {
          cargo: '#dea584',
          go: '#00add8',
          benchmarkjs: '#f1e05a',
          benchmarkluau: '#000080',
          pytest: '#3572a5',
          googlecpp: '#f34b7d',
          catch2: '#f34b7d',
          julia: '#a270ba',
          jmh: '#b07219',
          benchmarkdotnet: '#178600',
          customBiggerIsBetter: '#38ff38',
          customSmallerIsBetter: '#ff3838',
          _: '#333333'
        };

        function init() {
          function collectBenchesPerTestCase(entries) {
            const map = new Map();
            for (const entry of entries) {
              const {commit, date, tool, benches} = entry;
              for (const bench of benches) {
                const result = { commit, date, tool, bench };
                const arr = map.get(bench.name);
                if (arr === undefined) {
                  map.set(bench.name, [result]);
                } else {
                  arr.push(result);
                }
              }
            }
            return map;
          }

          const data = window.BENCHMARK_DATA;

          // Render header
          document.getElementById('last-update').textContent = new Date(data.lastUpdate).toString();
          const repoLink = document.getElementById('repository-link');
          repoLink.href = data.repoUrl;
          repoLink.textContent = data.repoUrl;

          // Render footer
          document.getElementById('dl-button').onclick = () => {
            const dataUrl = 'data:,' + JSON.stringify(data, null, 2);
            const a = document.createElement('a');
            a.href = dataUrl;
            a.download = 'benchmark_data.json';
            a.click();
          };

          // Prepare data points for charts
          return Object.keys(data.entries).map(name => ({
            name,
            dataSet: collectBenchesPerTestCase(data.entries[name]),
          }));
        }

        function renderAllChars(dataSets) {

          function renderGraph(parent, name, dataset) {
            const canvas = document.createElement('canvas');
            canvas.className = 'benchmark-chart';
            parent.appendChild(canvas);

            const color = toolColors[dataset.length > 0 ? dataset[0].tool : '_'];
            const data = {
              labels: dataset.map(d => d.commit.id.slice(0, 7)),
              datasets: [
                {
                  label: name,
                  data: dataset.map(d => d.bench.value),
                  borderColor: color,
                  backgroundColor: color + '60', // Add alpha for #rrggbbaa
                }
              ],
            };
            const options = {
              scales: {
                xAxes: [
                  {
                    scaleLabel: {
                      display: true,
                      labelString: 'commit',
                    },
                  }
                ],
                yAxes: [
                  {
                    scaleLabel: {
                      display: true,
                      labelString: dataset.length > 0 ? dataset[0].bench.unit : '',
                    },
                    ticks: {
                      beginAtZero: true,
                    }
                  }
                ],
              },
              tooltips: {
                callbacks: {
                  afterTitle: items => {
                    const {index} = items[0];
                    const data = dataset[index];
                    return '\n' + data.commit.message + '\n\n' + data.commit.timestamp + ' committed by @' + data.commit.committer.username + '\n';
                  },
                  label: item => {
                    let label = item.value;
                    const { range, unit } = dataset[item.index].bench;
                    label += ' ' + unit;
                    if (range) {
                      label += ' (' + range + ')';
                    }
                    return label;
                  },
                  afterLabel: item => {
                    const { extra } = dataset[item.index].bench;
                    return extra ? '\n' + extra : '';
                  }
                }
              },
              onClick: (_mouseEvent, activeElems) => {
                if (activeElems.length === 0) {
                  return;
                }
                // XXX: Undocumented. How can we know the index?
                const index = activeElems[0]._index;
                const url = dataset[index].commit.url;
                window.open(url, '_blank');
              },
            };

            new Chart(canvas, {
              type: 'line',
              data,
              options,
            });
          }

          function renderBenchSet(name, benchSet, main) {
            const setElem = document.createElement('div');
            setElem.className = 'benchmark-set';
            main.appendChild(setElem);

            const nameElem = document.createElement('h1');
            nameElem.className = 'benchmark-title';
            nameElem.textContent = name;
            setElem.appendChild(nameElem);

            const graphsElem = document.createElement('div');
            graphsElem.className = 'benchmark-graphs';
            setElem.appendChild(graphsElem);

            for (const [benchName, benches] of benchSet.entries()) {
              renderGraph(graphsElem, benchName, benches)
            }
          }

          const main = document.getElementById('main');
          for (const {name, dataSet} of dataSets) {
            renderBenchSet(name, dataSet, main);
          }
        }

        renderAllChars(init()); // Start
      })();
    </script>
  </body>
</html>
